package com.appkubes.fintech.restful.application.face.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.appkubes.fintech.restful.application.user.domain.UserCertificateDomain;
import com.appkubes.fintech.common.exption.BusinessException;
import com.appkubes.fintech.common.response.Response;
import com.appkubes.fintech.core.po.FintechUserBaseInfo;
import com.appkubes.fintech.core.po.FintechUserPersonal;
import com.appkubes.fintech.restful.application.face.service.FaceAuthService;
import com.appkubes.fintech.restful.application.user.service.UserBaseInfoService;
import com.appkubes.fintech.restful.application.user.service.UserPersonalService;
import com.appkubes.fintech.restful.util.Base64Utils;
import com.appkubes.fintech.restful.util.HttpUtils;
import com.appkubes.fintech.restful.util.InvertCodeGeneratorUitls;
import com.appkubes.fintech.restful.util.Sha1Uitls;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.apache.http.Consts;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;


@Service
@Slf4j
public class FaceAuthServiceImpl implements FaceAuthService {

    private static String version = "1.0.0";
    private static String access_token_url = "https://idasc.webank.com/api/oauth2/access_token?grant_type=client_credential";
    private static String api_ticket_url = "https://idasc.webank.com/api/oauth2/api_ticket";
    private static String faceid_url = "https://idasc.webank.com/api/server/h5/geth5faceid";
    private static String faceAuthUrl = "https://ida.webank.com/api/web/login";
    private static String syncUrl = "https://idasc.webank.com/api/server/sync";
    @Value("${idcard.faceauth.authCallBackUrl}")
    private String finishAuthCallbackUrl;
    @Value("${idcard.image.base.path}")
    private String IDCARD_IMAGE_BASE_PATH;

    @Value("${idcard.faceauth.appId}")
    private String wbappid;

    @Value("${idcard.faceauth.appKey}")
    private String wbappkey;

    private static final String REDIS_PREFIX_KYLIN_PLUGIN_FINTECH_FACE = "kylin_plugin_fintech:face:";
    @Autowired
    private UserBaseInfoService fintechUserBaseInfoService;
    @Autowired
    private UserPersonalService userPersonalService;
    @Autowired
    private UserCertificateDomain certificateService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Value("${idcard.image.base.path}")
    String imagePath;

    @Override
    public Response getFaceResult(String userKey) {
        try {
            String token = getAccessToken();
            String signticket = getSignTicket(token);

            //设置签名所需要的参数
            List<String> codes = InvertCodeGeneratorUitls.genCodes(32, 1);
            String nonce = codes.get(0);
            ArrayList<String> list = new ArrayList<String>();
            list.add(wbappid);
            list.add(version);
            list.add(nonce);
            list.add(userKey);
            String sign = Sha1Uitls.sign(list, signticket);

            String url = syncUrl + "?app_id=" + wbappid + "&nonce=" + nonce + "&order_no=" + userKey + "&version=1.0.0&sign=" + sign + "&get_file=2";
            String res = HttpUtils.sendGet(url, null);

            JSONObject resultJson = JSONObject.parseObject(res);
            String codeResult = resultJson.getString("code");
            if (codeResult.equals("0")) {
                //更新实名认证
                certificateService.updatePersonalCertificate(userKey, 2);

                JSONObject resultData = resultJson.getJSONObject("result");
                String photoBase64 = resultData.getString("photo");
                if(photoBase64 != null && !photoBase64.equals("")) {
                    String imgName = "/faceimg/face_" + userKey+".png";
                    Base64Utils.Base64ToImage(photoBase64,imagePath+imgName);
                    FintechUserPersonal userPersonal = userPersonalService.getUserPersonalDao().selectByPrimaryKey(userKey);
                    userPersonal.setFaceImg(imgName);
                    userPersonalService.getUserPersonalDao().updateByPrimaryKey(userPersonal);
                }

                return Response.success(res);
            } else {
                return Response.fail("人脸核身结果失败：" + resultJson.getString("msg"));
            }
        } catch (Exception e) {
            log.error("人脸核身结果查询异常", e);
            return Response.fail(e.getMessage());
        }
    }

    @Override
    public Response getFaceAuthUrl(String userKey) {
        try {
            List<String> codes = InvertCodeGeneratorUitls.genCodes(32, 2);
            String code = codes.get(0);

            FintechUserBaseInfo userBaseInfo = fintechUserBaseInfoService.getUserBaseInfoDao().selectByPrimaryKey(userKey);
            if (userBaseInfo == null) {
                throw new BusinessException("用户信息错误！");
            }
            //for test
//            userBaseInfo.setRealName("黄小");
//            userBaseInfo.setIdentityNum("431126199509050010");
//            userCertificate.setIdcardFrontView("/1578108898500.jpg");

            String orderNo = userKey;
            String idNo = userBaseInfo.getIdentityNum();
            String name = userBaseInfo.getRealName();
            String token = getAccessToken();
            String signticket = getSignTicket(token);
            String nonceTicket = getNONCETicket(token, userKey);
            String h5FaceIdStr = getH5FaceIdStr(userKey, idNo, name, signticket);
            ArrayList<String> list = new ArrayList<String>();
            list.add(wbappid);
            list.add(version);
            list.add(code);
            list.add(orderNo);
            list.add(h5FaceIdStr);
            list.add(userKey);
            String getqianming = Sha1Uitls.sign(list, nonceTicket);
            List<NameValuePair> params = Lists.newArrayList();
            params.add(new BasicNameValuePair("webankAppId", wbappid));
            params.add(new BasicNameValuePair("version", version));
            params.add(new BasicNameValuePair("nonce", code));
            params.add(new BasicNameValuePair("orderNo", orderNo));
            params.add(new BasicNameValuePair("h5faceId", h5FaceIdStr));
            params.add(new BasicNameValuePair("url", finishAuthCallbackUrl + "?userKey=" + userKey));
            params.add(new BasicNameValuePair("resultType", "2"));
            params.add(new BasicNameValuePair("userId", userKey));
            params.add(new BasicNameValuePair("sign", getqianming));
            //转换为键值对
            String str = EntityUtils.toString(new UrlEncodedFormEntity(params, Consts.UTF_8));
            //创建Get请求
            final String Url = faceAuthUrl + "?" + str;
            return Response.success(Url);
        } catch (Exception e) {
            log.error("人脸核身异常", e);
            return Response.fail(e.getMessage());
        }
    }

    private String getH5FaceIdStr(String userKey, String idNo, String name, String signticket) {
        FintechUserPersonal userPersonal = userPersonalService.getUserPersonalDao().selectByPrimaryKey(userKey);
        if (userPersonal == null || userPersonal.getIdcardFrontView().equals("")) {
            throw new BusinessException("用户身份证未上传！");
        }

        String orderNo = userKey;

        //设置签名所需要的参数
        ArrayList<String> list = new ArrayList<String>();
        list.add(wbappid);
        list.add(orderNo);
        list.add(name);
        list.add(idNo);
        list.add(userKey);
        list.add(version);
        String signStr = Sha1Uitls.sign(list, signticket);

        JSONObject params = new JSONObject();
        params.put("webankAppId", wbappid);
        params.put("orderNo", orderNo);
        params.put("name", name);
        params.put("idNo", idNo);
        params.put("userId", userKey);
        params.put("version", version);
        params.put("sign", signStr);

        //for test
        //String frontPath = IDCARD_IMAGE_BASE_PATH + "/bg.png";
        String frontPath = IDCARD_IMAGE_BASE_PATH + userPersonal.getIdcardFrontView();
        String sourcePhotoStr = resizeImageTo500K(frontPath);
        params.put("sourcePhotoStr", sourcePhotoStr);
        params.put("sourcePhotoType", "2");

        String res = HttpUtils.doPost(faceid_url, params.toString(), "utf-8");
        JSONObject resJson = JSONObject.parseObject(res);
        if (resJson.getString("code").equals("0")) {
            JSONObject resultJson = resJson.getJSONObject("result");
            String faceIdStr = resultJson.getString("h5faceId");
            return faceIdStr;
        } else {
            throw new BusinessException("验证失败！" + resJson.getString("msg"));
        }
    }

    //压缩图片小于500k
    public static String resizeImageTo500K(String frontPath) {
        try {
            BufferedImage output = Thumbnails.of(new File(frontPath)).size(800, 600).asBufferedImage();
            String base64 = Base64Utils.imageToBase64ByBuffered(output);
            while (base64.length() - base64.length() / 8 * 2 > 500000) {
                output = Thumbnails.of(output).scale(0.9f).asBufferedImage();
                base64 = Base64Utils.imageToBase64ByBuffered(output);
            }
            return Base64Utils.imageToBase64ByBuffered(output);
        } catch (Exception e) {
            log.error("照片压缩失败: ", e);
            throw new BusinessException("照片压缩失败");
        }
    }

    /**
     * 获取signticket,accessToken与signTicket配对缓存20分钟
     *
     * @return
     */
    @Override
    public String getSignTicket(String accessToken) {
        String key = REDIS_PREFIX_KYLIN_PLUGIN_FINTECH_FACE + "signTicket";
        String redisResult = stringRedisTemplate.opsForValue().get(key);
        if (redisResult != null) {
            return redisResult;
        }

        String url = api_ticket_url + "?type=SIGN&version=" + version + "&app_id=" + wbappid + "&access_token=" + accessToken;
        String res = HttpUtils.sendGet(url, null);

        JSONObject userJson = JSONObject.parseObject(res);

        if (userJson.getString("code").equals("0")) {
            JSONArray ticketArray = userJson.getJSONArray("tickets");
            JSONObject ticketJson = ticketArray.getJSONObject(0);
            String result = ticketJson.get("value").toString();
            stringRedisTemplate.opsForValue().set(key, result, 1200, TimeUnit.SECONDS);
            return result;
        } else {
            throw new BusinessException("signTicket 获取失败");
        }
    }

    /**
     * 获取token，accessToken与signTicket配对缓存20分钟
     *
     * @return
     */
    @Override
    public String getAccessToken() {
        String key = REDIS_PREFIX_KYLIN_PLUGIN_FINTECH_FACE + "accessToken";
        String redisResult = stringRedisTemplate.opsForValue().get(key);
        if (redisResult != null) {
            return redisResult;
        }

        String url = access_token_url + "&version=" + version + "&app_id=" + wbappid + "&secret=" + wbappkey;
        String res = HttpUtils.sendGet(url, null);

        JSONObject userJson = JSONObject.parseObject(res);
        if (userJson.getString("code").equals("0")) {
            String result = userJson.get("access_token").toString();
            stringRedisTemplate.opsForValue().set(key, result, 1200, TimeUnit.SECONDS);
            return result;
        } else {
            throw new BusinessException("accessToken 获取失败");
        }
    }

    /**
     * 获取nonceticket
     *
     * @param accessToken
     * @param userId
     * @return
     */
    @Override
    public String getNONCETicket(String accessToken, String userId) {
        String url = api_ticket_url + "?type=NONCE&version=" + version + "&app_id=" + wbappid + "&access_token=" + accessToken + "&user_id=" + userId;
        String res = HttpUtils.sendGet(url, null);

        JSONObject userJson = JSONObject.parseObject(res);
        JSONArray ticketArray = userJson.getJSONArray("tickets");
        JSONObject ticketJson = ticketArray.getJSONObject(0);
        String result = ticketJson.get("value").toString();

        return result;
    }
}
